package cn.keduox.dao.impl;

import cn.keduox.annotation.*;
import cn.keduox.dao.BaseDao;
import cn.keduox.utils.JDBCUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;

import java.sql.*;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2017/12/23 0023.
 */
public class BaseDaoImpl<T, E> implements BaseDao<T, E> {
    private Class<T> EntityClass; // 获取实体类
    private Class<E> EntityClass1;

    public BaseDaoImpl() {
        /**
         *  传递User就是 com.example.daoimp.BaseDaoImp<com.example.bean.User>
         *  传递Shop就是 com.example.daoimp.BaseDaoImp<com.example.bean.Shop>
         * */
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        /**
         * 这里如果传递的是User.那么就是class com.example.bean.User
         * 如果传递的是Shop.	   那么就是class com.example.bean.Shop
         * */
        EntityClass = (Class<T>) type.getActualTypeArguments()[0];
        EntityClass1 = (Class<E>) type.getActualTypeArguments()[1];
    }

    /**
     * 添加方法
     */
    @Override
    public int add(T t) throws Exception {
        /*INSERT into user1(id,sex) VALUES("1",1)*/
        //获得对象的字节码对象
        //获取表名注解
        Table table = (Table) EntityClass.getAnnotation(Table.class);
        String tableName = table.name();
        Map<String, Object> sqlMap = new HashMap<String, Object>();
        //获取列名与属性值
        Field[] fields = EntityClass.getDeclaredFields();
        for (Field field : fields) {
            //因为数据库中不会添加自定义的属性，所以需要对自定义的属性进行剪除,isAnnotationPresent判断是否有注解
            boolean isColumn = field.isAnnotationPresent(Column.class);
            if (isColumn) {
                field.setAccessible(true);// 施暴
                //获得列名
                Column column = field.getAnnotation(Column.class);
                String key = column.name();
                //获得值
         /* //另一种获取值得方式
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(),t.getClass());
            Object value=pd.getReadMethod().invoke(t);*/
                Method getMethod = EntityClass.getMethod("get" + getMethodName(field.getName()));
                Object value = getMethod.invoke(t);
                sqlMap.put(key, value);
            }
        }
        //遍历sqlMap,获取sql语句,并拼接sql
        String sql = "insert into " + tableName + "(";
        String sql1 = " values(";
        //将值放入一个集合中，方便预编译用
        List<Object> valueList = new ArrayList<>();
        //最佳的Map遍历方式
        for (Map.Entry<String, Object> entry : sqlMap.entrySet()) {
            sql = sql + entry.getKey() + ",";
            sql1 = sql1 + "?,";
            valueList.add(entry.getValue());
        }
        sql = sql.substring(0, sql.length() - 1) + ") " + sql1.substring(0, sql1.length() - 1) + ")";
        return sqlUpdate(sql, valueList);
    }

    /**
     * 查询方法:仅封装了where和默认的order by以及分页,支持模糊查询
     *
     * @param t     要查的表
     * @param open  是否启用排序
     * @param flag  排序要求
     * @param e     分页对象
     * @param vague 是否进行模糊查询
     * @return
     */
    @Override
    public List<T> select(T t, boolean open, boolean flag, E e, boolean vague,String order) throws Exception {
        //获取表名注解
        Table table = EntityClass.getAnnotation(Table.class);
        String tableName = table.name();
        //取的域对象并做遍历处理
        Field[] fields = EntityClass.getDeclaredFields();
        //存放where语句后的查询条件的Map
        Map<String, Object> whereMap = new HashMap<String, Object>();
        List<Object> sonObjectList = new ArrayList<>();
        //存放排序条件的集合
        List<String> orderList = new ArrayList<String>();
        for (Field field : fields) {
            field.setAccessible(true);//一定要施暴
            //获取属性值，判断是否为空,拼接where条件
            if (field.isAnnotationPresent(Column.class)) {
/*                Method method=clazz.getMethod("get"+getMethodName(field.getName()));Object value=method.invoke(object);PropertyDescriptor pd = new PropertyDescriptor(field.getName(),object.getClass());Object value=pd.getReadMethod().invoke(object);String verificationValue= (String) field.get(object);*/
                Method method = EntityClass.getMethod("get" + getMethodName(field.getName()));
                String verificationValue = String.valueOf(method.invoke(t));
                if (!verificationValue.equals("null")) {
                    //不为空,是查询条件
                    Object value = field.get(t);
                    Class clazz = value.getClass();
                    Column column = field.getAnnotation(Column.class);
                    String key = column.name();
                    whereMap.put(key, value);
                }
            }
            //拼接排序条件
            if (field.isAnnotationPresent(OrderItem.class)) {
                OrderItem orderItem = field.getAnnotation(OrderItem.class);
                orderList.add(orderItem.name());
            }
        }
        //拼接分页
        Integer[] limitCondition = new Integer[2];
        Field[] fields1 = EntityClass1.getDeclaredFields();
        for (Field field : fields1) {
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                String limitName = column.name();
                Method method = EntityClass1.getMethod("get" + getMethodName(field.getName()));
                Integer value = (Integer) method.invoke(e);
                if (value != null) {
                    if (limitName.equals("currentPage")) {
                        limitCondition[0] = value;
                    }
                    if (limitName.equals("currentCount")) {
                        limitCondition[1] = value;
                    }
                }
            }
        }
        if (limitCondition[0] != null && limitCondition[1] != null) {
            limitCondition[0] = limitCondition[1] * (limitCondition[0] - 1);
        }
        //集合准备完毕，开始拼接sql
        StringBuilder sql = new StringBuilder("");
        sql.append("select * from ").append(tableName);
        //判断whereMap是否为空来进行条件查询
        List<Object> valueList = new ArrayList<>();
        if (whereMap.size() != 0) {
            sql.append(" where ");
            for (Map.Entry<String, Object> entry : whereMap.entrySet()) {
                if (vague) {
                    if (entry.getValue() instanceof String) {
                        sql.append(entry.getKey() + " like ? and ");
                        valueList.add("%" + entry.getValue() + "%");
                    } else {
                        sql.append(entry.getKey() + "=? and ");
                        valueList.add(entry.getValue());
                    }
                } else {
                    sql.append(entry.getKey() + "=? and ");
                    valueList.add(entry.getValue());
                }
            }
            sql.delete(sql.length() - 5, sql.length());
        }
        //判断是否进行默认排序
        if (open) {
            sql.append(" order by ");
            if (orderList.size() != 0) {
                for (String str : orderList) {
                    sql.append(str).append(",");
                }
                sql.delete(sql.length() - 1, sql.length());
            }
        }
        //是否进行自定义排序
        if(!(order==null || order.equals(""))){
            if (open){
                boolean f=true;
                for (String str : orderList) {
                    if (str.equals(order)){
                        f=false;
                        break;
                    }
                }
                if (f){
                    sql.append(",").append(order+" ");
                }
            }else {
                sql.append(" order by ").append(order+" ");
            }
        }
        if (open || !(order==null || order.equals(""))){
            sql.append(" ").append(flag == true ? "asc" : "desc").append(" ");
        }
        //判断是否分页
        if (limitCondition[0] != null && limitCondition[1] != null) {
            sql.append("limit").append(" ").append(limitCondition[0]).append(",").append(limitCondition[1]);
        }
        //SQL拼接完毕,开始执行查询
        PreparedStatement preState = null;
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            //编译
            preState = connection.prepareStatement(String.valueOf(sql));
            preState = JDBCUtils.setObject(preState, valueList.toArray());
            resultSet = preState.executeQuery();
            //通过反射将resultSet封装为对象集
            List<T> resultList = new ArrayList<>();
            int m=resultSet.getRow();
            while (resultSet.next()) {
                T resultObject = EntityClass.newInstance();
                for (Field field : fields) {
                    field.setAccessible(true);
                    if (field.isAnnotationPresent(Column.class)) {
                        String columnName = field.getAnnotation(Column.class).name();
                        Object fieldValue = resultSet.getObject(columnName);
                        field.set(resultObject, fieldValue);
                    }
                }
                for (Field field : fields) {
                    //处理实体类注解，采用迭代
                    if (field.isAnnotationPresent(Entity.class)) {
                        //获取注解对象,获取对应的属性值
                        Entity entity = field.getAnnotation(Entity.class);
                        String idName = entity.field();
                        Method method = EntityClass.getMethod("get" + getMethodName(idName));
                        String verificationValue = String.valueOf(method.invoke(t));
                        Class clazz = Class.forName(entity.address());
                        Object sonObject = clazz.newInstance();
                        Object value;
                        if (!verificationValue.equals("null")) {
                            //这是条件查询
                            value = method.invoke(t);
                        } else {
                            //非条件查询
                            value = method.invoke(resultObject);
                        }
                        Field[] sonFields = sonObject.getClass().getDeclaredFields();
                        //获取子实体类的id属性
                        for (Field field1 : sonFields) {
                            field1.setAccessible(true);//一定要施暴
                            //获取外键
                            if (field1.isAnnotationPresent(Key.class)) {
                                if (field1.getAnnotation(Key.class).farther().equals(entity.table())) {
                                    field1.set(sonObject, value);
                                    break;
                                }
                            }
                        }
                        sonObjectList = sonSelect(sonObject);
                        //封装
                        //判断当前域对象的类型用getSimpleName
                        Class c = field.getType();
                        String name = c.getSimpleName();
                        if (sonObjectList.size() != 0 && sonObjectList != null) {
                            if ("List".equals(name)) {
                                field.set(resultObject, sonObjectList);
                            } else {
                                field.set(resultObject, sonObjectList.get(0));
                            }
                        }
                    }
                }
                resultList.add(resultObject);
            }
            return resultList;
        } catch (SQLException e1) {
            e1.printStackTrace();
            return null;
        } finally {
            //关闭资源
            JDBCUtils.close(connection, preState, resultSet);
        }
    }

    /**
     * 迭代处理类
     */
    public List<Object> sonSelect(Object object) throws Exception {
        Class clazz = object.getClass();
        Table table = (Table) clazz.getAnnotation(Table.class);
        String tableName = table.name();
        Field[] fieldList = clazz.getDeclaredFields();
        Map<String, Object> whereMap = new HashMap<>();
        List<Object> sonObjectList = new ArrayList<>();
        for (Field field : fieldList) {
            field.setAccessible(true);
            //普通类型注解
            if (field.isAnnotationPresent(Column.class)) {
                Method method = clazz.getMethod("get" + getMethodName(field.getName()));
                String verificationValue = String.valueOf(method.invoke(object));
                if (!verificationValue.equals("null")) {
                    Object value = method.invoke(object);
                    String key = field.getAnnotation(Column.class).name();
                    whereMap.put(key, value);
                }
            }
        }
        //集合准备完毕，开始拼接sql
        StringBuilder sql = new StringBuilder("");
        sql.append("select * from ").append(tableName);
        List<Object> valueList = new ArrayList<>();
        if (whereMap.size() != 0) {
            sql.append(" where ");
            for (Map.Entry<String, Object> entry : whereMap.entrySet()) {
                sql.append(entry.getKey() + "=? and ");
                valueList.add(entry.getValue());
            }
            sql.delete(sql.length() - 5, sql.length());
        }
        //SQL拼接完毕,开始执行查询
        PreparedStatement preState = null;
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            //编译
            preState = connection.prepareStatement(String.valueOf(sql));
            preState = JDBCUtils.setObject(preState, valueList.toArray());
            resultSet = preState.executeQuery();
            //通过反射将resultSet封装为对象集
            List<Object> resultList = new ArrayList<>();
            while (resultSet.next()) {
                Object resultObject = clazz.newInstance();
                for (Field field : fieldList) {
                    field.setAccessible(true);
                    //普通注解
                    if (field.isAnnotationPresent(Column.class)) {
                        String columnName = field.getAnnotation(Column.class).name();
                        Object fieldValue = resultSet.getObject(columnName);
                        field.set(resultObject, fieldValue);
                    }
                }
                for (Field field : fieldList) {
                    //处理实体类注解，采用迭代
                    if (field.isAnnotationPresent(Entity.class)) {
                        Entity entity = field.getAnnotation(Entity.class);
                        String idName = entity.field();
                        Method method = clazz.getMethod("get" + getMethodName(idName));
                        String verificationValue = String.valueOf(method.invoke(object));
                        Class clazz1 = Class.forName(entity.address());
                        Object sonObject = clazz1.newInstance();
                        Object value;
                        if (!verificationValue.equals("null")) {
                            //条件查询
                            value = method.invoke(object);
                        } else {
                            value = method.invoke(resultObject);
                        }
                        Field[] sonFields = sonObject.getClass().getDeclaredFields();
                        for (Field field2 : sonFields) {
                            field2.setAccessible(true);//一定要施暴
                            if (field2.isAnnotationPresent(Key.class)) {
                                if (field2.getAnnotation(Key.class).farther().equals(entity.table())) {
                                    field2.set(sonObject, value);
                                    break;
                                }
                            }
                        }
                        sonObjectList = sonSelect(sonObject);
                        //封装
                        //判断注解是否是加在集合上的
                        //判断当前域对象的类型用getSimpleName
                        Class c = field.getType();
                        String name = c.getSimpleName();
                        if (sonObjectList.size() != 0 && sonObjectList != null) {
                            if ("List".equals(name)) {
                                field.set(resultObject, sonObjectList);
                            } else {
                                field.set(resultObject, sonObjectList.get(0));
                            }
                        }
                    }
                }
                resultList.add(resultObject);
            }
            return resultList;
        } catch (SQLException e1) {
            e1.printStackTrace();
            return null;
        } finally {
            //关闭资源
            JDBCUtils.close(connection, preState, resultSet);
        }
    }

    /**
     * 修改方法,通过ID修改
     */
    @Override
    public int update(T t) throws Exception {
        String tableName = EntityClass.getAnnotation(Table.class).name();
        //获取域对象
        Field[] fields = EntityClass.getDeclaredFields();
        Map<String, Object> updateMap = new HashMap<>();
        Map<String, Object> idMap = new HashMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Column.class)) {
                String verificationValue = String.valueOf(field.get(t));
                if (!verificationValue.equals("null")) {
                    //值不为空才更新
                    String key = field.getAnnotation(Column.class).name();
                    Object value = field.get(t);
                    updateMap.put(key, value);
                }
            }
            if (field.isAnnotationPresent(Id.class)) {
                String key = field.getAnnotation(Id.class).name();
                Object value = field.get(t);
                idMap.put(key, value);
            }
        }
        //拼接SQL
        StringBuilder sql = new StringBuilder("");
        sql.append("update ").append(tableName).append(" set ");
        List<Object> valueList = new ArrayList<>();
        if (updateMap.size() != 0) {
            for (Map.Entry<String, Object> entry : updateMap.entrySet()) {
                sql.append(entry.getKey()).append("=?,");
                valueList.add(entry.getValue());
            }
        }
        sql.delete(sql.length() - 1, sql.length()).append(" where ");
        if (idMap.size() == 1) {
            for (Map.Entry<String, Object> entry : idMap.entrySet()) {
                sql.append(entry.getKey()).append("=?");
                valueList.add(entry.getValue());
            }
        }
        return sqlUpdate(String.valueOf(sql), valueList);
    }

    /**
     * 封装Update方法
     */
    private static int sqlUpdate(String sql, List<Object> valueList) {
        PreparedStatement preState = null;
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            //编译
            preState = connection.prepareStatement(sql);
            preState = JDBCUtils.setObject(preState, valueList.toArray());
            return preState.executeUpdate();
        } catch (SQLException e) {
            return -1;
        } finally {
            //关闭资源
            JDBCUtils.close(connection, preState, resultSet);
        }
    }

    /**
     * 把一个字符串的第一个字母大写、效率是最高的
     */
    private static String getMethodName(String fildeName) throws Exception {
        byte[] items = fildeName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }

}
